﻿#include "stdafx.h"

#include "svgwriter.h"

#include <QFile>

namespace
{
	QString _ColorToRGBA(const QColor& c)
	{
		return QString("rgb(%1, %2, %3, %4)").arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alphaF());
	}
}

SvgWriter::SvgWriter(const SvgShapeVec& shapes, QTextStream& stream, double height, double width)
	: m_shapes(shapes)
	, m_stream(stream)
	, m_height(height)
	, m_width(width)
{
}

void SvgWriter::Save()
{
	_WriteStart();

	for (const auto& shape : m_shapes)
		shape->Accept(*this);

	_WriteEnd();
}

void SvgWriter::SetBackground(const QColor& color)
{
	m_background = color;
}

bool SvgWriter::Save(const SvgShapeVec& shapes, const QString& fileName, const QColor& background, double height, double width)
{
	QFile file(fileName);
	if (!file.open(QFileDevice::WriteOnly))
		return false;

	QTextStream stream(&file);

	SvgWriter writer(shapes, stream, height, width);
	writer.SetBackground(background);

	writer.Save();
	return true;
}

void SvgWriter::_VisitLine(SvgLine& line)
{
	const QLineF& l = line.Line();
	m_stream << "<line"
		<< " x1=\"" << l.p1().x() << '"'
		<< " y1=\"" << l.p1().y() << '"'
		<< " x2=\"" << l.p2().x() << '"'
		<< " y2=\"" << l.p2().y() << '"';
	_WritePenProp(line.Pen());
	m_stream << "/>";
}

void SvgWriter::_VisitRect(SvgRect& rect)
{
	const QRectF r = rect.Rect();
	m_stream << "<rect"
		<< " x=\"" << r.x() << '"'
		<< " y=\"" << r.y() << '"'
		<< " width=\"" << r.width() << '"'
		<< " height=\"" << r.height() << '"';
	_WriteShapeEnd(rect);
}

void SvgWriter::_VisitEllipse(SvgEllipse& ellipse)
{
	m_stream << "<ellipse"
		<< " cx=\"" << ellipse.Center().x() << '"'
		<< " cy=\"" << ellipse.Center().y() << '"'
		<< " rx=\"" << ellipse.Rx() << '"'
		<< " ry=\"" << ellipse.Ry() << '"';
	_WriteShapeEnd(ellipse);
}

void SvgWriter::_VisitPolygon(SvgPolygon& polygon)
{
	m_stream << "<polygon"
		<< " fill-rule=\"" << FILL_RULE_STR << '"'
		<< " points=\"";

	for (const auto& p : polygon.Polygon())
		m_stream << p.x() << ", " << p.y() << " ";
	m_stream << '"';
	_WriteShapeEnd(polygon);
}

void SvgWriter::_VisitText(SvgText& text)
{
	const QPointF& p = text.Pos();
	const QFont& font = text.Font();

	m_stream << "<text"
		<< " x=\"" << p.x() << '"'
		<< " y=\"" << p.y() << '"'
		<< " fill=\"" << _ColorToRGBA(text.Pen().color()) << '"'
		<< " font-family=\"" << font.family() << '"'
		<< " font-size=\"" << font.pixelSize() << '"'
		<< " xml:space=\"preserve\""
		<< ">"
		<< text.Text().toHtmlEscaped()
		<< "</text>";
}

void SvgWriter::_VisitPath(SvgPath& path)
{
	const QPainterPath& d = path.Path();
	m_stream << "<path"
		<< " fill-rule=\"" << FILL_RULE_STR << '"'
		<< " d=\"";

	for (int i = 0; i < d.elementCount(); ++i)
	{
		QPainterPath::Element e = d.elementAt(i);

		if (e.isMoveTo())
			m_stream << "M " << e.x << ' ' << e.y << ' ';
		else if (e.isLineTo())
			m_stream << "L " << e.x << ' ' << e.y << ' ';
		else if (e.isCurveTo())
			m_stream << "C " << e.x << ' ' << e.y << ' ';
		else
			m_stream << ", " << e.x << ' ' << e.y << ' '; // 这个是贝塞尔曲线的额外数据 
	}

	m_stream << '"';
	_WriteShapeEnd(path);
}

void SvgWriter::_WriteStart()
{
	m_stream << R"(<svg xmlns="http://www.w3.org/2000/svg" version="1.1" subset="wps")"
		<< " height=\"" << m_height << "\""
		<< " width=\"" << m_width << "\""
		<< ">";

	if (m_background.isValid())
	{
		SvgRect rect(QRectF(0, 0, m_width, m_height));
		rect.Pen().setColor(m_background);
		rect.Brush().setColor(m_background);
		rect.Accept(*this);
	}
}

void SvgWriter::_WriteEnd() const
{
	m_stream << "</svg>";
}

void SvgWriter::_WriteShapeEnd(SvgShape& shape) const
{
	_WritePenProp(shape.Pen());
	_WriteBrushProp(shape.Brush());
	m_stream << "/>";
}

void SvgWriter::_WritePenProp(const QPen& pen) const
{
	m_stream
		<< " stroke=\"" << _ColorToRGBA(pen.color()) << '"'
		<< " stroke-width=\"" << pen.widthF() << '"';
}

void SvgWriter::_WriteBrushProp(const QBrush& brush) const
{
	m_stream
		<< " fill=\"" << _ColorToRGBA(brush.color()) << '"';
}
